/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


/*************************************************************************
 * File: python.h
 *************************************************************************
 *\author: starlove
 *************************************************************************/

#ifndef __MLSE_SCRIPT_PYTHON_H__
#define __MLSE_SCRIPT_PYTHON_H__

#ifdef _DEBUG
#undef _DEBUG
#define DEBUG_FLAG_OPEN
#endif // _DEBUG

#include <Python.h>

#ifdef DEBUG_FLAG_OPEN
#define _DEBUG
#undef DEBUG_FLAG_OPEN
#endif // DEBUG_FLAG_OPEN

#include <platform/platform.h>

#ifdef __MLSE_SCRIPT_PARALLEL__
#include <stackless_api.h>
#endif

#include <pyport.h>

#include <platform/timer.h>
#include <script/script.h>
#include <codec/python_codec.h>
#include <network/peer.h>
#include <network/network_wrapper.h>

#define __MLSE_SCRIPT_CMD_LIMIT__ 1024
#define __MLSE_SOCKET_SEND_SWAP_BUFFER_SIZE__ (1024 * 1024)

#ifdef _DEBUG
#define __MLSE_PYTHON_SCRIPTMACHINE_VERSION__ "1.0.0 Debug"
#else
#define __MLSE_PYTHON_SCRIPTMACHINE_VERSION__ "1.0.0 Release"
#endif

enum __enumCallbackType
{
	eCBTP_NONE 		= 0,
	eCBTP_CONNECT 	= 1,
	eCBTP_CLOSE 	= 2,
	eCBTP_PACKET 	= 3,
	eCBTP_HTTP 		= 4
};

class CPythonMachine : public IScriptMachine
{
public:
	CPythonMachine();
	virtual bool Initialize(int tp = MACHINE_SERVER);
	virtual bool Finalize();
	virtual bool Bind(const char *, const char *);
	virtual void *OnPacket(SOCKET, LPPACKET);
	virtual bool OnHttp(SOCKET, LPBUFFER_WRAPPER);
	virtual void *OnConnect(SOCKET, const char *, unsigned int);
	virtual void *OnClose(SOCKET);
public:
	void __Finalize();
	PyObject *Eval(const char *, ...);
	PyObject *LoadModule(const char *);
	PyObject *GetDictMember(PyObject *, const char *);
	PyObject *GetModuleMember(PyObject *, const char *);
	PyObject *GetModuleMemberEx(PyObject *, PyTypeObject *, const char *);
	PyObject *GetModuleCallbleMember(PyObject *, const char *);

	void SetCallback(int, PyObject *);
	PyObject *SendToPeer(int, PyObject *);
	PyObject *SendToPeer(int, void *, unsigned int);
	IPeer *GetPeer(int);
	void SendHttpRetToPeer(int, PyObject *, const char *, const char *);
	void PreSendData(LPNETWORKBUFFER buf, PyObject *);
	bool InitModule();
	bool InitClientModule();
private:
	CPythonCodec m_pyCodec;
	PyObject *m_pyOnPacketFunc;
	PyObject *m_pyOnHttpFunc;
	PyObject *m_pyOnConnectFunc;
	PyObject *m_pyOnCloseFunc;
	#ifdef __MLSE_SCRIPT_PARALLEL__
	PyTaskletObject *m_pyOnPacketFuncTask;
	PyTaskletObject *m_pyOnHttpFuncTask;
	PyTaskletObject *m_pyOnConnectFuncTask;
	PyTaskletObject *m_pyOnCloseFuncTask;
	#endif
	char m_szSendSwapBuf[__MLSE_SOCKET_SEND_SWAP_BUFFER_SIZE__];
};

class CPyTimer: public ITimer
{
public:
	CPyTimer():m_pyOnTimerFunc(NULL),m_pyOnTimerParam(NULL){ __timer.id = GetTimestamp(); };
	virtual void OnTimer(unsigned long);
	void SetOnTimer(unsigned long, char, PyObject *, PyObject *);
private:
	PyObject *m_pyOnTimerFunc;
	PyObject *m_pyOnTimerParam;
};

#define PYCLASS_INIT(OBJ,n)\
	const char *OBJ::m_name = n

#define PYCLASS_BEGIN_METHOD_DEF(OBJ)\
static PyMethodDef OBJ##_cls_methods[] = {


#define PYCLASS_ADD_METHOD(name, fn)\
    {name,         (PyCFunction)fn,      METH_CLASS|METH_VARARGS},

#define PYCLASS_END_METHOD_DEF(OBJ)\
    {NULL,NULL,0}\
};\
PyMethodDef *OBJ::m_methods = OBJ##_cls_methods;

#define PYCLASS_DEF_INTERFACE(O)\
public:\
	static PyObject *NewInstance(PyObject *pBase, PyObject *args);\
	static void Dealloc(O *self);\
	static PyObject* GetAttr(O *self, const char* name);\
	static PyMethodDef *m_methods;\
	static const char *m_name;

#define PYCLASS_IMP_INTERFACE(O)\
void O::Dealloc(O *self)\
{\
	self->pMachine = NULL;\
    PyObject_Free(self);\
}\
PyObject* O::GetAttr(O *self,const char* name)\
{\
    return Py_FindMethod(O::m_methods,(PyObject*)self,name);\
}\
PyObject* O::NewInstance(PyObject *pBase, PyObject *args)\
{\
	LPCPyClassBase pClassBase = (LPCPyClassBase)pBase;\
	if(!pClassBase)\
		Py_RETURN_NONE;\
    O *p = PyObject_NEW(O, CPyClassBase::GetTypeObject<O>());\
    if( !p )\
        Py_RETURN_NONE;\
	p = new(p) O(pClassBase->pMachine);\
	p->pMachine = pClassBase->pMachine;\
    return (PyObject*)p;\
    Py_RETURN_NONE;\
}

typedef struct __CPyClassBase
{
    PyObject_HEAD
	CPythonMachine *pMachine;

	static void Dealloc(__CPyClassBase *self){};
	static PyObject* GetAttr(__CPyClassBase *self, const char* name){ Py_RETURN_NONE; };
	static const char *m_name;
	template<typename T>
	static PyTypeObject *GetTypeObject()
	{
		static PyTypeObject typeinfo = {
			PyObject_HEAD_INIT(&PyType_Type)
			0,/*ob_size*/
			T::m_name,						/*tp_name*/
			sizeof(T),               /*tp_basicsize*/
			0,                              /*tp_itemsize*/
			/* methods */
			(destructor)T::Dealloc,        /*tp_dealloc*/
			0,                              /*tp_print*/
			(getattrfunc)T::GetAttr,        /*tp_getattr*/
			0,								/*tp_setattr*/
			0,                              /*tp_compare*/
			0,                              /*tp_repr*/
			0,                              /*tp_as_number*/
			0,                              /*tp_as_sequence*/
			0,                              /*tp_as_mapping*/
			0,                              /*tp_hash*/
		};
		return &typeinfo;
	};
}CPyClassBase, *LPCPyClassBase;

class CPyModule
{
public:
	CPyModule(){};
	CPyModule(const char *module_name, CPythonMachine *pMachine = NULL);
	void RegisterIntValue(const char *name, int v){ m_intValues.insert(std::pair<const char *,int>(name, v)); };
	void RegisterCallable(const char *name, PyCFunction pyFunc, int flag);
	void Embed();
	template <typename T>
	void RegisterClass(const char *name, int flag = METH_NOARGS)
	{ RegisterCallable(name, (PyCFunction)T::NewInstance, flag); };
private:
	const char *m_name;
	std::vector<PyMethodDef> m_methods;
	std::map<const char *, int> m_intValues;
	static LPCPyClassBase m_pClassBase;
};

class CPyServer : public CPyClassBase
{
PYCLASS_DEF_INTERFACE(CPyServer)
public:
	CPyServer(CPythonMachine *pM){ pMachine = pM; m_pyOnHttpFunc = NULL; }
	static PyObject *SetCallback(CPyServer *self, PyObject* args);
	static PyObject *HttpProcess(CPyServer *self, PyObject* args);
	static PyObject *Send(CPyServer *self, PyObject* args);
	void SetHttpFunc(PyObject *pyObj){ m_pyOnHttpFunc = pyObj; Py_XINCREF(pyObj); };
	PyObject *GetHttpFunc(){ return m_pyOnHttpFunc; };
private:
	PyObject *m_pyOnHttpFunc;
};

class CPyClient : public CPyClassBase
{
PYCLASS_DEF_INTERFACE(CPyClient)
public:
	CPyClient(CPythonMachine *pM){
		pMachine = pM;
		m_peer.Init((ISocket *)&m_socket, INVALID_SOCKET);
		m_peer.SetMachine(pM);
	}
	IPeer *GetPeer(){ return (IPeer *)&m_peer; };
	static PyObject *SetCallback(CPyClient *self, PyObject* args);
	static PyObject *Connect(CPyClient *self, PyObject* args);
	static PyObject *Send(CPyClient *self, PyObject* args);
	static PyObject *Lookup(CPyClient *self, PyObject* args);
	static PyObject *Close(CPyClient *self);
private:
	CPeer m_peer;
	CMLSESocket m_socket;
};

extern PyObject* CreateTimer(PyObject *self, PyObject* args);
extern PyObject* DestoryTimer(PyObject *self, PyObject* args);
extern PyObject* GetMachineVersion(PyObject *self);

#endif // __MLSE_SCRIPT_PYTHON_H__
